#include "Random.h"
#include "LEDStrip.h"

#include <string.h>

static void Test();
static void Illumnation();
static void Starfield1();
static void Starfield2();
static void Waves();
static void Rainbow();
static void Chase();

static void ROM_SysCtlDelay() {}

static uint32_t strip[240];

int main(void)
{
	InitializeLEDStrip();

	//Test();
	//Illumnation();
	//Starfield1();
	Starfield2();
	//Waves();
	//Rainbow();
	// Chase();
}

static inline int triangle(int x,int period,int minamplitude,int maxamplitude)
{
	int amplitude=maxamplitude-minamplitude;
	int phase=x%period;
	if(phase<0) phase+=period;
	int saw=2*phase*amplitude/period;
	if(saw>amplitude) return 2*amplitude-saw+minamplitude;
	else return saw+minamplitude;
}

static inline uint32_t RandomBrightColour()
{
	int c1=RandomInteger()&0x7f;
	int c2=RandomInteger()&0x7f;
	switch(RandomInteger()%3)
	{
		case 0: return RGB(0x7f,c1,c2);
		case 1: return RGB(c2,0x7f,c2);
		case 2: return RGB(c1,c2,0x7f);
	}
}

static inline uint32_t RandomBrightSaturatedColour()
{
	int c=RandomInteger()&0x7f;
	switch(RandomInteger()%6)
	{
		case 0: return RGB(0x7f,c,0);
		case 1: return RGB(c,0x7f,0);
		case 2: return RGB(0,0x7f,c);
		case 3: return RGB(0,c,0x7f);
		case 4: return RGB(c,0,0x7f);
		case 5: return RGB(0x7f,0,c);
	}
}

static void Test()
{
	int t=0;
	for(;;)
	{
		for(int i=0;i<240;i++)
		{
			int r=(i+t)&127;
			int g=(i+t)&127;
			int b=(i+t)&127;

			strip[i]=RGB(r,g,b);
		}

		UpdateLEDStrip(strip);

		ROM_SysCtlDelay(80000000/3/60);

		t+=1;
	}
}


static void Illumnation()
{
	for(int i=0;i<240;i++) strip[i]=RGB(0x7f,0x7f,0x7f);
	UpdateLEDStrip(strip);
	for(;;);
}

static void Starfield1()
{
	int numstars=0;
	int maxstars=100;
	int32_t position[maxstars];
	int32_t velocity[maxstars];
	uint32_t colour[maxstars];

	int t=0;
	for(;;)
	{
		if((RandomInteger()&255)==0 && numstars<maxstars)
		{
			int max=0x10000;
			int min=0x2000;
			int32_t vel=max*min/(RandomInteger()%(max-min)+min);
			if(RandomInteger()&1)
			{
				position[numstars]=0;
				velocity[numstars]=vel;
			}
			else
			{
				position[numstars]=240<<16;
				velocity[numstars]=-vel;
			}

			colour[numstars]=RandomBrightSaturatedColour();
			numstars++;
		}

		if((t++&3)==0)
		for(int i=0;i<240;i++)
		{
			strip[i]=(strip[i]>>1)&0x7f7f7f;
		}

		for(int i=0;i<numstars;i++)
		{
			position[i]+=velocity[i];
			if(position[i]>=240<<16 || position[i]<=0)
			{
				numstars--;
				position[i]=position[numstars];
				velocity[i]=velocity[numstars];
				colour[i]=colour[numstars];
				continue;
			}

			int pixel=position[i]>>16;
			strip[pixel]=colour[i];
		}

		UpdateLEDStrip(strip);

		ROM_SysCtlDelay(80000000/3/200);
	}
}

static void Starfield2()
{
	int numstars=0;
	int maxstars=100;
	int32_t position[maxstars];
	int32_t velocity[maxstars];
	int32_t acceleration[maxstars];
	uint32_t colour[maxstars];

	int max=0x10000;
	int min=0x2000;
	int drag=8;

	int t=0;
	for(;;)
	{
		if((RandomInteger()&255)==0 && numstars<maxstars)
		{
			int32_t vel=max*min/(RandomInteger()%(max-min)+min);
			if(RandomInteger()&1)
			{
				position[numstars]=0;
				velocity[numstars]=vel;
				acceleration[numstars]=vel>>drag;
			}
			else
			{
				position[numstars]=240<<16;
				velocity[numstars]=-vel;
				acceleration[numstars]=-(vel>>drag);
			}

			colour[numstars]=RandomBrightSaturatedColour();
			numstars++;
		}

		if((t++&3)==0)
		for(int i=0;i<240;i++)
		{
			strip[i]=(strip[i]>>1)&0x7f7f7f;
		}

		for(int i=0;i<numstars;i++)
		{
			if((RandomInteger()&1023)==0)
			{
				int32_t vel=max*min/(RandomInteger()%(max-min)+min);
				if(RandomInteger()&1) acceleration[i]=vel>>drag;
				else acceleration[i]=-vel>>drag;
			}

			velocity[i]+=acceleration[i];
			velocity[i]-=velocity[i]>>drag;
			position[i]+=velocity[i];

			if(position[i]>=240<<16 || position[i]<=0)
			{
				numstars--;
				position[i]=position[numstars];
				velocity[i]=velocity[numstars];
				colour[i]=colour[numstars];
				continue;
			}

			int pixel=position[i]>>16;
			strip[pixel]=colour[i];
		}

		UpdateLEDStrip(strip);

		ROM_SysCtlDelay(80000000/3/200);
	}
}

static void Waves()
{
	int32_t positions[240];
	int32_t velocities[240];

	memset(positions,0,sizeof(positions));
	memset(velocities,0,sizeof(velocities));

	int dropspeed=100000;

	velocities[RandomInteger()%240]+=dropspeed;

	for(;;)
	{
		if((RandomInteger()&511)==0)
		{
			dropspeed=-dropspeed;
			velocities[RandomInteger()%240]+=dropspeed;
		}

		for(int i=0;i<240;i++)
		{
			positions[i]+=velocities[i];
		}

		for(int i=0;i<240;i++)
		{
			velocities[i]-=velocities[i]/256;
		}

		for(int i=0;i<240;i++)
		{
			int left;
			if(i!=0) left=positions[i-1];
			else left=0;

			int right;
			if(i!=239) right=positions[i+1];
			else right=0;

			velocities[i]+=(left+right-2*positions[i])/32;
		}

		for(int i=0;i<240;i++)
		{
			if(positions[i]>0) strip[i]=RGB(0,0,positions[i]/65536);
			else strip[i]=RGB(-positions[i]/65536,0,0);
		}

		UpdateLEDStrip(strip);

		ROM_SysCtlDelay(80000000/3/400);
	}
}


static void Rainbow()
{
	int t=0;
	for(;;)
	{
		for(int i=0;i<240;i++)
		{
			int r=triangle(i+t,       80,-128,255);
			int g=triangle(i+t+80/3,  80,-128,255);
			int b=triangle(i+t+80*2/3,80,-128,255);

//			int r=triangle(i+t,80,0,191)-64;
//			int g=triangle(i+t+80/3,80,0,191-64);
//			int b=triangle(i+t+160/3,80,0,191-64);

//			int r=triangle(i+t,80,0,128);
//			if(r<64) r=0; else r=127;
//			int g=triangle(i-t,90,0,128);
//			if(g<64) g=0; else g=127;
//			int b=0;

			strip[i]=RGB(r,g,b);
		}

		UpdateLEDStrip(strip);

		ROM_SysCtlDelay(80000000/3/60);

		t+=1;
	}
}

static void Chase()
{
	int numparticles=1;
	int pos[numparticles];
	int dir[numparticles];

	for(int i=0;i<numparticles;i++)
	{
		pos[i]=0;
		dir[i]=1;
	}

	for(;;)
	{
		for(int i=0;i<240;i++)
		{
			strip[i]=(strip[i]>>1)&0x7f7f7f;
		}

		for(int i=0;i<numparticles;i++)
		{
			int r=RandomInteger()&0x7f;
			strip[pos[i]]=RGB(r,0,0);

			if(pos[i]>=239) dir[i]=-1;
			if(pos[i]<=0) dir[i]=1;
			pos[i]+=dir[i];

			strip[pos[i]]=RGB(127,127,127);
		}

		UpdateLEDStrip(strip);

		ROM_SysCtlDelay(80000000/3/60);
	}
}

